/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sismol.embarcado;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;

import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Math.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import sismol.cartography.Posicao;
import sismol.data.NMEAException;

/**
 *
 * @author Rafael
 */
public class SerialReader implements Runnable {

    private FileWriter fstreamIn;
    private BufferedWriter inFile;
    private NMEAParser nmeaParser = new NMEAParser();
    private List<String> messageTypes;
    private SerialPort serialPort;
    private InputStream in;
    private volatile boolean stop = false;
    private ServerConnector serverConnector;
    private GUIWriter guiWriter;
    private Posicao pAnterior = null;
    private Double distancia = 0.0d;
    public SerialReader(GUIWriter guiWriter, ServerConnector serverConnector) {
        this.messageTypes = new ArrayList<String>();
        this.messageTypes.add("RMC");
        try {
            int i = 0;
            while (true) {
                File f = new File("out" + i + ".txt");
                if (f.exists()) {
                    i++;
                } else {
                    break;
                }
            }
            fstreamIn = new FileWriter("in" + i + ".txt");
            inFile = new BufferedWriter(fstreamIn);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        this.guiWriter = guiWriter;
        this.serverConnector = serverConnector;
    }

    void connect(String portName) throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException {
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        CommPort commPort = portIdentifier.open(this.getClass().getName(), 2000);

        serialPort = (SerialPort) commPort;
        serialPort.setSerialPortParams(38400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
        this.in = serialPort.getInputStream();
    }

    void stop() throws Exception {
        this.stop = true;

        // Aguarda 500 ms para a Thread parar e fecha portas e arquivos
        Timer t = new Timer(true);
        t.schedule(new TimerTask() {

            public void run() {
                try {
                    in.close();
                    inFile.close();
                    serialPort.close();
                } catch (Exception e) {
                    //TODO Tratar exceção direito
                    throw new RuntimeException();
                }
            }
        }, 500);
    }

    public void run() {
        byte[] buffer = new byte[1024];
        int len = -1;

        String bufferString;
        String sentence = new String();
        try {
            while (!stop && (len = this.in.read(buffer)) > -1) {
                bufferString = new String(buffer, 0, len);
                inFile.write(bufferString);
                inFile.flush();

                if (bufferString.contains("$")) {

                    String[] parts = bufferString.split("\\$");
                    // Se parts.length = 0, bufferString é apenas um $
                    if (parts.length == 0) {
                        sendData(sentence);
                        sentence = "$";
                    } else {
                        for (int i = 0; i < parts.length; i++) {
                            if (i != 0 || parts[0].equals("")) {
                                sentence = sentence.concat(parts[i]);
                            }
                            if (i != parts.length - 1 || bufferString.charAt(bufferString.length() - 1) == '$') {
                                sendData(sentence);
                                sentence = "$";
                            }
                        }

                    }
                } else {
                    sentence = sentence.concat(bufferString);
                }
            }
        } catch (IOException e) {
            //TODO tratar exceção
            e.printStackTrace();
        }
    }

    /**
     * Avalia uma se uma sentença NMEA é valida e de um determinado tipo
     *
     * @param sentence - Sentença a ser avaliada
     * @return true se a sentença é do tipo indicadi
     */
    private boolean isSentence(String sentence) {
        if (sentence.length() > 7) {
            String sentenceIdentifier = sentence.substring(3, 6);

            if (messageTypes.contains(sentenceIdentifier)) {
                return true;
            }
        }
        return false;
    }

    private void sendData(String sentence) {
        if (isSentence(sentence)) {
            try {
                sentence = sentence.replace("\r", "");
                sentence = sentence.replace("\n", "");
                Posicao p = nmeaParser.parsePosicao(sentence);
                if(pAnterior != null){
                    nmeaParser.calculateDistance(pAnterior, p);
                }
                this.serverConnector.sendData(p, distancia);
                guiWriter.writeGUI(sentence, p);

            } catch (NMEAException e) {
                //TODO tratar exceção
//                throw new RuntimeException(e);
                System.out.println("NMEAException");
                System.out.println(e.toString());
            } catch (Exception e) {
                System.out.println("Exceção");
                System.out.println(e.toString());
            }


        }
    }


    public GUIWriter getGuiWriter() {
        return guiWriter;
    }

    public void setGuiWriter(GUIWriter guiWriter) {
        this.guiWriter = guiWriter;
    }

    public ServerConnector getServerConnector() {
        return serverConnector;
    }

    public void setServerConnector(ServerConnector serverConnector) {
        this.serverConnector = serverConnector;
    }
}
